%%%-------------------------------------------------------------------
%%% File    : p34.erl
%%% Author  : Plamen Dragozov <plamen at dragozov.com>
%%% Description : 
%%% 145 is a curious number, as 1! + 4! + 5! = 1 + 24 + 120 = 145.
%%% Find the sum of all numbers which are equal to the sum of the 
%%% factorial of their digits.
%%%
%%% Note: as 1! = 1 and 2! = 2 are not sums they are not included.
%%%
%%%
%%% Created : 27 Dec 2008
%%%-------------------------------------------------------------------
-module(p34).

%% API
-compile(export_all).

% 9! = 362880.
%  9999999 -> 2540160 :7
% 99999999 -> 2903040 :8 -> no number >= 2540160 fits
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: 
%% Description:
%%--------------------------------------------------------------------
solution() ->
    lists:sum(step(1, 11, 2, digit_factorials(), [])). 
%%====================================================================
%% Internal functions
%%====================================================================
digit_factorials() ->
    A1 = array:set(0, 1, array:new(10)),
    lists:foldl(fun(X, Acc) -> array:set(X, X * array:get(X - 1, Acc), Acc) end, A1, lists:seq(1, 9)).

%iterate through all numbers and collect the ones equal to the sum of their digit factorials
step(_, 2540160, _, _, Acc) -> Acc;
step(9, LastNumber, _, Factorials, Acc) ->
    NewNumber = LastNumber + 1,
    NewSum = number_to_facsum(NewNumber, Factorials, 0),
    %io:format("~p,~p~n", [NewNumber, NewSum]), 
    NewAcc = case NewSum =:= NewNumber of
                 true -> [NewNumber|Acc];
                 _ -> Acc
             end,
    step(0, NewNumber, NewSum, Factorials, NewAcc);
step(LastDigit, LastNumber, LastSum, Factorials, Acc) ->
    NewNumber = LastNumber + 1,
    NewDigit = LastDigit + 1,
    NewSum = LastSum + array:get(NewDigit, Factorials) - array:get(LastDigit, Factorials),
    %io:format("~p,~p~n", [NewNumber, NewSum]), 
    NewAcc = case NewSum =:= NewNumber of
                 true -> [NewNumber|Acc];
                 _ -> Acc
             end,
    step(NewDigit, NewNumber, NewSum, Factorials, NewAcc).

%convert a number to the sum of the factorials of its digits
number_to_facsum(0, _, Acc) ->
    Acc;
number_to_facsum(N, Factorials, Acc) ->
    number_to_facsum(N div 10, Factorials, array:get(N rem 10, Factorials) + Acc).
